//
//  A002ViewController.swift
//  SyanYan
//
//  Created by Yanyan Jiang on 2018/7/5.
//  Copyright © 2018年 Yanyan Jiang. All rights reserved.
//

import UIKit

class A002ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        optionalNumberPractice()
        self.memoryManagement()

    }
    
    //可能值练习
    func optionalNumberPractice() -> () {
        
        //可能值、可能值绑定、可能值链
        //optionals  可能值
        //Memory management 内存管理
        //Initialization  初始化函数
        //Closures   闭包
        //Pattern matching 模式匹配
        
        //字符串到整型的转换 String to Integer
        //请输入您的年龄：这个年龄我们无法控制用户输入的是字符串还是文字，所以说就会需要可能值这个概念
        //toInt()这个方法没有办法返回一个返回值，以前可能会用到 NULL Int Max  NSNotFound -1 0 Nil nil  用这种方式表达它没有返回值
        //let age = response.toInt()
        
        //可能值类型专门用于来表达它没有值  Optional Type 可能值类型
        //如果你想声明这个值是可能值的话
        var optionalNumber: Int?
        //默认初始化为nil  optionalNumber它就是一个可能值类型
        //oc中的nil 与swift中的nil不同
        //oc中的nil 是一个指针指向空对象时的nil  只用于表达指针类型；但是在swift中用于指向任何东西它没有值，可以对一个对象赋nil值，也可以对一个整型赋nil值
        optionalNumber = 6 //这就是可能值的第二个状态，可能值有值，这个可能值本身并不是6，而是可能值包着一个6
        //可能值有两种状态一种是nil 一种是有值，这个值被包装在可能值中，使用的时候需要把可能值拆包，拆包过后才能使用这个6
        
        
        //非可能值类型 就是一般的类型，任何一个不加问号的类型都是非可能值类型
        let myString: String = "好长的一个字符串"
        var myObject: UILabel = UILabel()
        myObject.backgroundColor = .blue
        myObject.text = myString
        //非可能值类型不能为nil
        //var myString: String = nil //Nil cannot initialize specified type 'String'
        //如果你想把nil 赋值给一个变量 ，这个变量必须是可能值
        //有可能返回值为nil 所以返回值后需要加问号
        func findIndexOfString(string: String, array: [String]) ->Int?{
            for (index, value) in array.enumerated() {
                if(value == string){
                    return index
                }
            }
            return nil
        }
        
        let celebrities = ["三桑","摩托","安卓","鸡鸭"]
        var indexVVV:Int?
        indexVVV = findIndexOfString(string: "安卓", array: celebrities)
        if indexVVV != nil {
            print("indexVVV\(indexVVV!)")
        }
        let index = findIndexOfString(string: "安卓", array: celebrities)
        if (index != nil) {
            print("\(celebrities[index!]), 你好")//对nil是没有办法去拆包所以要判断一下，
        }else{
            print("没这人")
        }
        //在检测的同时进行解包 Optional Bingding
        if let indexValue = index { //可能值的绑定 首先查index有没有值 如果index有值它会把 index的值拆出来赋值到左边的indexValue,而我们左边的indexValue就不是可能值类型了，就是一般的整型了，indexValue就是可能值绑定
            print("\(celebrities[indexValue]), 你好")//对nil是没有办法去拆包所以要判断一下，
        }else{
            print("没这人")
        }
        
        if let index = findIndexOfString(string: "安卓", array: celebrities) {
            print("\(celebrities[index]), 你好")//对nil是没有办法去拆包所以要判断一下，index的类型是 Int
        }else{
            print("没这人")
        }
        
        
        let alex = Person()
        let residence = Residence()
        alex.residence = residence
        let address = Address()
        address.buildingNumber = "2125"
        address.streetName = "Rue St Marc"
        residence.address = address
        //如果我想从最左边的person取到最右边的address的buildingNumber值,我可能会按照下面的方式做
        var addressNumber: Int?
        if let resi = alex.residence {
            if let addr = resi.address{
                if let buildingNumber = addr.buildingNumber{
                    if let convertedNumber = Int(buildingNumber){
                        addressNumber = convertedNumber
                    }
                }
            }
        }
        if (addressNumber != nil){
            print("addressNumber:\(addressNumber!)")
        }
        
        //这样写就太麻烦了，如果swift语言变成这样就太恶心了
        //swift中有一个新的技术可以解决这个问题，这个技术就叫可能值链 Optional Chaining
        let optionalStr: String? = alex.residence?.address?.buildingNumber
        if optionalStr != nil {
            addressNumber = Int(optionalStr!)
            print("addressNumber:\(addressNumber!)")
        }
        
        //可能值链、可能值绑定  Optional Chaining、 Optional Building 同时使用
        //结合可能值绑定使用同时拆包
        if (alex.residence?.address?.buildingNumber) != nil{
        }
        
        //可能值的内部结构就是一个带泛型的枚举
        enum Optional<T>{
            case None
            case Some(T)
        }
        
        //使用可能值技术，可以安全的操纵一些可能没有值的变量
        //如果没有值，就是nil
        //如果有值，这个值被包装在可能值中
        //对可能值进行拆包，才可以访问内部存储的那个不是nil的值
        //在你确认有值的时候，再使用强制拆包运算符(!)
        //使用 if let 可能值绑定技术，可达到同时检测和拆包的目的
        //可能值链(?)是一种简洁的方式去应对一串可能值
        
    }
    
    //内存管理 进阶
    func memoryManagement() -> () {
        //内存管理、自动引用计数、弱引用、非持有引用
        //内存管理 memory management
        //基本概念：swift中使用的是自动引用计数管理对象的内存
        //引用计数：每个对象都有一个引用计数，来记录有多少人(对象或变量)需要该对象活着
                 //当引用计数>0的时候，该对象会保留在内存中(代表有别人需要它)
                 //当引用计数 =0的时候，该对象会被立刻从内存中销毁(代表没有人需要它)
        //自动引用计数: 通过简单的标记，声明各种引用的类型，系统会在变以前填补引用计数的管理代码
        //注意 swift中不支持手动内存管理(没有人使用它的时候会立刻被释放)、自动引用计数不是垃圾回收机制(就是一周一次大扫除第二天回复原装的感觉)
        //Automatic Reference Counting 自动引用计数
        func play(count: Int){
            let left = Bottle()
            if count>1 {
                var right = Bottle()
                right = left
            }//变量right域结束
        }//变量left 域结束
        //而不是等待垃圾回收某一个时刻去检查,发现没有指针指向的对象时 一起从内存中消失
        
        //ownership 所有权
        var renters = ["三丧": Person()]
        var apts = [507: Apartment()]
        renters["三丧"]!.moveIn(apt: apts[507]!)
        //如果我们想使用一个对象是通过一个对象的引用指针来找到这个对象的，如果没有一个指针指向这个对象那这个对象你在内存中是用永远也找不到的
        renters["三丧"] = nil
        apts[507] = nil  //这样会引起内存泄露，因为这些内存在程序中永远无法被消除掉，除非你把程序关掉重开
        //weak 来的时候是空的走的时候也是空的
        
        //Using Weak References 使用弱引用
        //弱引用一定是可能值类型 弱引用指向的对象销毁掉了，它会把弱引用指向的对象置为nil ,因为我们一般的变量是没有办法变为空的；如果想设为空值只有可能值才有空值，所以弱引用一定是可能值类型
        //对可能值进行绑定时会产生一个瞬间的强引用
        let apt = Apartment()
        //等号左边的person是强引用，person在大括号外面的时候就会被销毁了；apt.person是弱引用；
        //person就会在if语句的大括号中访问的正常了，person就看成是一般的变量类型，局部变量的感觉，局部变量都是强引用
        if let person = apt.person  {
            print("可能值链也会产生强引用")
        }
        //可能值链也会产生强引用,这个强引用只在这一行中生效，超过这一行就没效果了，可能值链只是上面三行代码的一个缩写
        apt.person?.moveIn(apt: Apartment())
        
        //如果你只检测弱引用并不会产生强引用
        if apt.person != nil {
            //因为没有产生强引用下面这句话是没有用的 ，因为apt.person已经被置空了
            apt.person!.moveIn(apt: Apartment())
        }
        //可能值链在绑定时所产生的强引用并不会被保存到下一行代码
        apt.person?.moveIn(apt: Apartment())
        apt.person?.greet()//在这一行的时候可能就不会执行greet() 这个方法，大家要注意这一点
        
        
        //同生命线的关系 Same-Lifetime Relationships
        var customers = ["三丧": Person()]
        customers["三丧"]!.saveCard() //Person中有一个属性 card强引用指向 CreditCard ,信用卡中的holder会指回Person但是这个引用是非持有引用
        customers["三丧"] = nil //如果person死掉 那么creditCard系统不会把它设置为空，它会跟着person一起死掉
        //如何使用非持有引用，如果你发现有一个属性声明为非持有引用你可以正常的使用它，就像赋值你可以把它赋值给一个变量，你不用在这里进行拆包，因为非持有引用不是可能值类型，你可以在这里使用非持有引用调方法，就像正常的强引用一样使用它就可以了 let holder = card.holder card.holder.charge(2,19)
        //强引用、弱引用、非持有引用
        //强引用 对一般对象所属情况使用强引用，我一个对象底下有两个属性是两个对象，这两个对象底下有分几个对象，正向的这些引用我们都可以声明为强引用，那么怎么声明强引用那，就是你什么都不写就是强引用了
        //对不同生命线的对象之间的关系使用弱引用，我左边的对象创建有一个流程，我右边的对象创建是有一个流程，比如这两边的生死，比如左边对象的死掉与右边对象的死掉是没有关系的，比如人的创建与公寓的创建，人的生死生孩子与公寓的生死建房子是两码事，对于这两码事之间的对象关系我们可以使用弱引用进行链接。弱引用链接的效果就是当其中一个方向的生命线死掉的时候，我们左边这些对象指向右边的对象的引用就是自动被系统设置为空这个时候你就没有值了
        //非持有引用 对同生命线的反向引用使用非持有引用 比如说第二个对象必须需要第一个对象的存活它才能活着，第三个对象对第二个对象也是如此，如果第三个对象想使用第二对象的方法，你有第二对象的引用才能调方法，这时候我们的反向引用可以时候非持有引用，比如我第一个对象的属性不再指向中间的对象，那么第二个与第三个对象就会死掉
        //强引用 一般的指向对象的变量都是强引用，比如：局部变量、对象属性
        //弱引用或非持有引用
        //当引用需要反向指回时，如果还使用强引用会产生引用回路，从而内存泄露
        //这个时候需要选择使用弱引用或者非持有引用
        //弱引用和持有引用都不会对引用计数产生影响（我要用到你但不在乎你的死活）
        //弱引用可以为空，非持有引用不会
        //一般特殊情况选择弱引用
        
        
    }
    
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

}


//信用卡得有人，信用卡一赋值就不会变，人死了估计这信用卡也就没用了
//weak let holder: Person 改成weak这个弱引用就会出问题，如果你用weak的话当前的变量一定是可能值变量，系统会在人死了之后会将holder这个属性置空，所以为了接受空值，holder值的类型必须是可能值的类型weak let holder: Person? 如果它是可能值的话系统会在将来修改它的值为空，也就代表它不能成为常量,要成为var  weak var holder: Person 如果这么写的话就破坏了之前的规律，人如果死了信用卡也就没有必要存在了，信用卡中的属性是指向持有它的人，也就是说信用卡是依赖人的存在也存在，信用卡的属性中有个指向它依赖的对象，如果人死了的话，我们也没有必要让holder置为空,因为人死了信用卡也可以死掉了，所以我们没有必要写weak 的这个holder置为空。这时系统为我们提供了一个新的关键字叫做“非持有引用” unowned 它与weak一样不会使引用计数加1，但是他有第二个特点，他会一直有值，系统不会将它的值设置为空如果对方死掉的话
class CrediCard {
    //let holder: Person
    unowned let holder: Person
    init(holder: Person) {
        self.holder = holder
    }
}


class Apartment {
//    var person: Person? //一个公寓里可能住着一个人
    weak var person: Person? //我这个person属性依然可以指向一个person对象   weak修饰的person变量所指向的person对象并不会把所指向的对象引用计数加1，我指向的对象不会管你是死的还是活的，我不会保证你必须活，我不会把你的引用计数加190
}

//Optional Binding 可能值绑定
class Person {
    var residence: Residence?
//    var home: Apartment? //一个人可能住在一个公寓中
    weak var home: Apartment? //一个人可能住在一个公寓中
    var card:CrediCard?//信用卡人可以有信用卡也可以没有信用卡
    
    func moveIn(apt: Apartment) {
        self.home = apt
        apt.person = self
    }
    func greet() {
        print("你好")
    }
    func saveCard() -> () {
        card = CrediCard(holder: Person())
    }
}

class Residence {
    var address: Address?
}

class Address {
    var buildingNumber: String?
    var streetName: String?
    var apartmentNumber: String?
}

class Bottle {
    
}












